/*********************************************************************************
 *      Copyright:  (C) 2022 guoyi<675088383@qq.com>
 *                  All rights reserved.
 *
 *       Filename:  temperature_server.c
 *    Description:  This file 
 *                 
 *        Version:  1.0.0(2022年4月6日)
 *         Author:  guoyi<675088383@qq.com>
 *      ChangeLog:  1, Release initial version on "2022年4月5日 15时58分16秒"
 *                 
 **********************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include <pthread.h>
#include <getopt.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <sys/resource.h>

#include "sqlite3.h"
#include "cjson/cJSON.h"
#include "zlog.h"
#include "conf.h"
#include "common.h"

#define MAX_EVENTS 512
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))

zlog_category_t    *zc = NULL;

static inline void print_usage(char *progname);
int parse_json(char *json_buf, int bytes, sample_data_t *data);

int main(int argc, char **argv)
{
	int                 listen_fd, conn_fd;
	int                 daemon_run = 0;
	char                *progname = NULL;
	int                 opt;
	int                 rv = -1, i;
	char                buf[1024];
    char               *conf_file = "/home/iot22/guoyi/temperature/etc/all.conf";
    static sqlite3     *db = NULL;
    char               *tb_name = "Temperature";
	int                 epoll_fd;
	struct epoll_event  event;
	struct epoll_event  event_array[MAX_EVENTS];
	int                 events;
    sample_data_t       data;
    server_ctx_t        conf;

	struct option       long_options[] =
	{
		{"daemon", no_argument, NULL, 'b'},
		{"help", no_argument, NULL, 'h'},
		{NULL, 0, NULL, 0}
	};

	progname = basename(argv[0]);

	while ((opt = getopt_long(argc, argv, "bh", long_options, NULL)) != -1)
	{
		switch (opt)
		{
			case 'b':
				daemon_run = 1;
				break;
			case 'h':
				print_usage(progname);
				return EXIT_SUCCESS;
			default:
				break;
		}
	}

    if( server_parser_conf(conf_file, &conf) < 0 ) 
    {   
        printf("Parser config failure\n");
        return -1; 
    }   

    rv = zlog_init(conf.logconf);
    if( rv )
    {   
        printf("zlog_init() failure\n");
        zlog_fini();
        return -2; 
    }   

    zc = zlog_get_category("my_cat");
    if( !zc )
    {   
        printf("zlog_get_category() failure\n");
        zlog_fini();
        return -3; 
    }   
    printf("Initial zlog successfully\n");

	set_socket_rlimit();

	if( (listen_fd = socket_listen(NULL, conf.serv_port)) < 0 )
	{
		log_warn("ERROR: %s server listen on port %d failure\n", argv[0], conf.serv_port);
		return -2;
	}
	log_debug("%s server start to listen on port %d\n", progname, conf.serv_port);

	if( daemon_run )
	{
		daemon(0, 0);
	}

	if( (epoll_fd = epoll_create(MAX_EVENTS)) < 0 )
	{
		log_warn("epoll_create() failure: %s\n", strerror(errno));
		return -3;
	}

	event.events = EPOLLIN;
	event.data.fd = listen_fd;
	if( epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &event) < 0)
	{
		log_warn("epoll add listen socket failure: %s\n", strerror(errno));
		return -4;
	}

    if( (db_init(&db, conf.db_name, tb_name)) < 0 )
    {
        goto CleanUp;
    }

	while(1)
	{
		events = epoll_wait(epoll_fd, event_array, MAX_EVENTS, -1);
		if(events < 0)
		{
			log_warn("epoll failure: %s\n", strerror(errno));
			break;
		}
		else if(events == 0)
		{
			log_warn("epoll get timeout\n");
			continue;
		}
		
		for(i=0; i<events; i++)
		{
			if ( (event_array[i].events&EPOLLERR) || (event_array[i].events&EPOLLHUP) )
			{
				log_warn("epoll_wait get error on fd[%d]: %s\n", event_array[i].data.fd, strerror(errno));
				epoll_ctl(epoll_fd, EPOLL_CTL_DEL, event_array[i].data.fd, NULL);
				close(event_array[i].data.fd);
			}

			if( event_array[i].data.fd == listen_fd )
			{
				if( (conn_fd=accept(listen_fd, (struct sockaddr *)NULL, NULL)) < 0)
				{
					log_warn("accept new client failure: %s\n", strerror(errno));
					continue;
				}

				event.data.fd = conn_fd;
				event.events = EPOLLIN;

				if( epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_fd, &event) < 0 )
				{
					log_warn("epoll add client socket failure: %s\n", strerror(errno));
					close(event_array[i].data.fd);
					continue;
				}
				log_debug("epoll add new client socket[%d] ok.\n", conn_fd);
			}
			else
			{
                memset(buf, 0, sizeof(buf));
				if( (rv=read(event_array[i].data.fd, buf, sizeof(buf))) <= 0)
				{
					log_warn("socket[%d] read failure or get disconncet and will be removed.\n",event_array[i].data.fd);
					epoll_ctl(epoll_fd, EPOLL_CTL_DEL, event_array[i].data.fd, NULL);
					close(event_array[i].data.fd);
					continue;
				}

				else
				{
					log_debug("socket[%d] read get %d bytes data\n", event_array[i].data.fd, rv);
                    log_debug("The massege is:\n%s\n", buf);

                    parse_json(buf, sizeof(buf), &data);
                    db_push_record(db, tb_name, &data, sizeof(data));
				}
			}
		}
	}
CleanUp:
	close(listen_fd);
    db_term(db);
    zlog_fini();
    log_warn("Cleanup completed\n");
	return 0;
}

static inline void print_usage(char *progname)
{
	printf("Usage: %s [OPTION]...\n", progname);
	printf("%s is a socket server program, which used to verify client and echo back string from it\n",
			progname);
	printf("\nMandatory arguments to long options are mandatory for short options too:\n");
	printf(" -b[daemon] set program running on background\n");
	printf(" -h[help] Display this help information\n");
	printf("\nExample: %s -b -p 8900\n", progname);
	return ;
}

int parse_json(char *json_buf, int bytes, sample_data_t *data)
{
    int                 rv = 0;
    char                temper_str[32] = {0};
    cJSON              *sn = NULL;
    cJSON              *time = NULL;
    cJSON              *temper = NULL;
    cJSON              *json = NULL;

    if( !json_buf || !data )
    {   
        log_warn("ERROR: Invalid input arguments\n");
        return -1; 
    }   

    if( NULL == (json = cJSON_Parse(json_buf)) )
    {   
        log_warn("ERROR: cJSON_Parse() failure\n");
        return -2; 
    }

    sn = cJSON_GetObjectItem(json, "sn");
    time = cJSON_GetObjectItem(json, "datetime");
    temper = cJSON_GetObjectItem(json, "temper");

    if( !sn || !time || !temper )
    {   
        log_warn("ERROR: cJSON_AddItemToObject() failure\n");
        rv = -3; 
        goto OUT;
    }

    strncpy(data->sn, sn->valuestring, sizeof(data->sn));
    strncpy(data->time, time->valuestring, sizeof(data->time));
    snprintf(temper_str, sizeof(temper_str), "%.3f", atof(temper->valuestring));
    data->temper = atof(temper_str);

OUT:
    cJSON_Delete(json);

    return rv;
}
